﻿using System.ComponentModel.Composition;
using System.Linq;
using Mono.Cecil;

namespace WeavingCommon
{
    [Export, PartCreationPolicy(CreationPolicy.Shared)]
    public class MsCoreReferenceFinder
    {
        ModuleReader moduleReader;
        IAssemblyResolver assemblyResolver;
        public MethodReference EqualsMethodReference { get; set; }
        public MethodReference PropertyChangedEventHandlerInvokeReference { get; set; }
        public TypeReference BoolTypeReference { get; set; }
        public TypeReference VoidTypeReference { get; set; }
        public TypeReference ObjectTypeReference { get; set; }
        public TypeReference PropertyChangedEventHandlerReference { get; set; }
        public TypeReference PropertyChangedEventArgsReference { get; set; }
        public TypeReference StringTypeReference { get; set; }
        public MethodReference PropertyChangedEventConstructorReference { get; set; }
        public MethodReference ActionConstructorReference { get; set; }
        public MethodReference ObjectConstructor { get; set; }
        public TypeReference ActionTypeReference { get; set; }   
        public MethodDefinition NullableEqualsMethod { get; set; }
        public TypeReference GenericNullable { get; set; }

        [ImportingConstructor]
        public MsCoreReferenceFinder(ModuleReader moduleReader, IAssemblyResolver assemblyResolver)
        {
            this.moduleReader = moduleReader;
            this.assemblyResolver = assemblyResolver;
        }


        public void Execute()
        {
            var msCoreLibDefinition = assemblyResolver.Resolve("mscorlib");
            var msCoreTypes = msCoreLibDefinition.MainModule.Types;

            var objectDefinition = msCoreTypes.First(x => x.Name == "Object");
            var module = moduleReader.Module;
            ObjectTypeReference = module.Import(objectDefinition);
            var equalsDefinition = objectDefinition.Methods.First(x => x.Name == "Equals" && x.IsStatic);
            EqualsMethodReference = module.Import(equalsDefinition);
            var constructorDefinition = objectDefinition.Methods.First(x => x.IsConstructor);
            ObjectConstructor = module.Import(constructorDefinition);


             
            var nullableDefinition = msCoreTypes.FirstOrDefault(x => x.Name == "Nullable");
            NullableEqualsMethod = module.Import(nullableDefinition).Resolve().Methods.First(x=>x.Name == "Equals");

            var nullableGenericDefinition = msCoreTypes.FirstOrDefault(x => x.Name == "Nullable`1");
            GenericNullable = module.Import(nullableGenericDefinition);


            var boolDefinition = msCoreTypes.First(x => x.Name == "Boolean");
            BoolTypeReference = module.Import(boolDefinition);

            var voidDefinition = msCoreTypes.First(x => x.Name == "Void");
            VoidTypeReference = module.Import(voidDefinition);

            var stringDefinition = msCoreTypes.First(x => x.Name == "String");
            StringTypeReference = module.Import(stringDefinition);


            var systemDefinition = assemblyResolver.Resolve("System");
            var systemTypes = systemDefinition.MainModule.Types;

            var actionDefinition = msCoreTypes.FirstOrDefault(x => x.Name == "Action");
            if (actionDefinition == null)
            {
                actionDefinition = systemTypes.FirstOrDefault(x => x.Name == "Action");
            }
            if (actionDefinition == null)
            {
                var systemCoreDefinition = assemblyResolver.Resolve("System.Core");
                actionDefinition = systemCoreDefinition.MainModule.Types.FirstOrDefault(x => x.Name == "Action");
            }
            ActionTypeReference = module.Import(actionDefinition);
            var actionConstructor = actionDefinition.Methods.First(x => x.IsConstructor);
            ActionConstructorReference = module.Import(actionConstructor);


            var propChangedHandlerDefinition = systemTypes.First(x => x.Name == "PropertyChangedEventHandler");
            PropertyChangedEventHandlerReference = module.Import(propChangedHandlerDefinition);

            PropertyChangedEventHandlerInvokeReference = module.Import(propChangedHandlerDefinition.Methods.First(x => x.Name == "Invoke"));


            var propChangedArgsDefinition = systemTypes.First(x => x.Name == "PropertyChangedEventArgs");
            PropertyChangedEventArgsReference = module.Import(propChangedArgsDefinition);


            PropertyChangedEventConstructorReference = module.Import(propChangedArgsDefinition.Methods.First(x => x.IsConstructor));

        }
    }
}